<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace buwang\traits;

use app\manage\model\Token;
use buwang\service\CacheService;
use buwang\service\UserService;
use Firebase\JWT\JWT;
use think\contract\Arrayable;

/**
 * JWT
 * Class JwtService
 * @package service
 */
trait JwtTrait
{
    use ErrorTrait;

    public static function getTokenConfig()
    {
        $jwtConfig = config('jwt');
        $param = array(
            "iss" => $jwtConfig['iss'],
            "aud" => $jwtConfig['aud'],
            "iat" => time(),
            "nbf" => time(),//生效时间
        );
        return $param;
    }

    /**
     * 获取accessToken
     * @param array $data
     * @param string $scopes // mini_program app h5 official system
     * @return array|bool
     */
    public static function getAccessToken(array $data, string $scopes = 'mini_program')
    {
        if (!$data['id']) return self::setError('生成accessToken所需data数据必须传入用户id');
        $accessToken = self::getTokenConfig();
        $jwtConfig = config('jwt');
        $time = time();
        $accessToken['data'] = $data;
        $accessToken['scopes'] = $scopes;
        $accessToken['exp'] = $time + $jwtConfig['accessTokenExp'];
        $token = JWT::encode($accessToken, $jwtConfig['key'], $jwtConfig['alg']);
        $return = ['token' => $token, 'expires_time' => $accessToken['exp'], 'expires_in' => $jwtConfig['accessTokenExp']];
        //加入session缓存 附加token参数
        $data['token'] = $token;
        UserService::setLogin($data);//过期时间session.php里面设置 默认1h
        //加入redis缓存
        $base_login_mode = bw_config('base.base_login_mode', 1);//允许账号同时登录=>1 不允许=>2
        $base_login_number = bw_config('base.base_login_number', 600);//欲过期刷新时间 单位s
        $res = $res1 = $res2 = $res3 = true;
        //redis入库key值 防止后台和前端用户ID重复
        $userType = in_array($scopes, ['mini_program', 'app', 'h5', 'official']) ? 'user' : $scopes;
        $redisKey = $userType . $data['id'];

        $tokens = CacheService::getTokenCache($redisKey);
        if (isset($tokens)) {
            if ($base_login_mode == 2) {
                //CacheService::clearToken($tokens[0]);//清除原来的
                foreach ($tokens as $key => $value) {
                    CacheService::clearToken($tokens[$key]);//清除原来的
                }
                $res1 = CacheService::setTokenCache($redisKey, [$token], (int)$return['expires_in'], $userType);
            } else {
                array_push($tokens, $token);
                $tokens = array_unique($tokens);
                $res2 = CacheService::setTokenCache($redisKey, $tokens, (int)$return['expires_in'], $userType);
            }
        } else {
            CacheService::setTokenCache($redisKey, [$token], (int)$return['expires_in'], $userType);
        }
        $res3 = CacheService::setTokenCache($token, $return, (int)$return['expires_in'], $userType);
        $res = $res1 && $res2 && $res3 && true;
        if (!$res) return self::setError('redis保存token失败');
        //加入redis缓存结束
        return $return;
    }

    /**
     * 创建初始token
     * @param $data
     * @return array 返回accessToken 和 refreshToken
     */
    public static function initToken(array $data)
    {
        $tokenList = [
            'accessToken' => self::getAccessToken($data),
            'refreshToken' => self::getRefreshToken($data),
            'tokenType' => config('jwt.tokenType') //token_type：表示令牌类型，该值大小写不敏感，这里用bearer
        ];
        return $tokenList;
    }

    /**
     * 获取refreshToken
     * @param array $data
     * @param string $scopes
     * @return array
     */
    public static function getRefreshToken(array $data, string $scopes = 'mini_program')
    {
        $refreshToken = self::getTokenConfig();
        $jwtConfig = config('jwt');
        $time = time();
        $refreshToken['data'] = $data;
        $refreshToken['scopes'] = $scopes;
        $refreshToken['exp'] = $time + $jwtConfig['refreshTokenExp'];
        $token = ['token' => JWT::encode($refreshToken, $jwtConfig['key'], $jwtConfig['alg']), 'expires_time' => $refreshToken['exp'], 'expires_in' => $jwtConfig['refreshTokenExp']];//date('Y-m-d H:i:s',$refreshToken['exp'])
        return $token;
    }

    /**
     * 根据refreshToken刷新accessToken
     * @param string $refreshToken
     * @return string
     */
    public static function refreshAccessToken(string $refreshToken)
    {
        $decoded = self::decodeToken($refreshToken);
        if (isError($decoded)) {
            return $decoded;
        }
        return self::getAccessToken($decoded['data']);
    }


    /**
     * token解密
     * @param string $token
     * @return object
     */
    public static function decodeToken(string $token)
    {
        $jwtConfig = config('jwt');
        JWT::$leeway = 60; //当前时间减去60，把时间留点余地
        $decoded = JWT::decode($token, $jwtConfig['key'], array($jwtConfig['alg']));//解密方式，这里要和签发的时候对应
        return $decoded;
    }
}